// Monitor.cpp : implementation file
//
//////////////////////////////////////////////////
// CMonitor - wrapper to Win32 multi-monitor API
//
// Author: Donald Kackman
// Email:  dkackman_2000@yahoo.com
// Copyright 2002, Donald Kackman
//
// You may freely use or modify this code provided this
// Copyright is included in all derived versions.
//
///////////////////////////////////////////////////


#include "stdafx.h"
#include "MultiMonitor.h"
#include "Monitors.h"

// CMonitor

// constucts a monitor class not attached to any handle
CMonitor::CMonitor() :
	m_hMonitor(NULL)
{
}

// copy constructor
CMonitor::CMonitor(const CMonitor& monitor)
{
	m_hMonitor = (HMONITOR) monitor;
}

CMonitor::~CMonitor()
{
}

void CMonitor::Attach(const HMONITOR hMonitor)
{
	ASSERT( CMonitors::IsMonitor( hMonitor ) );

	m_hMonitor = hMonitor;
}

HMONITOR CMonitor::Detach()
{
	HMONITOR hMonitor = m_hMonitor;
	m_hMonitor = NULL;
	return hMonitor;
}

// creates an HDC for the monitor
// it is up to the client to call DeleteDC
//
// for normal multimonitor drawing it is not necessary to get a
// dc for each monitor. Windows takes care of drawing correctly
// on all monitors
//
// Only very exacting applications would need a DC for each monitor
HDC CMonitor::CreateDC() const
{
	ASSERT( IsMonitor() );

	CString name;
	GetName(name);

	//create a dc for this display
	HDC hdc = ::CreateDC(name, name, NULL, NULL);
	ASSERT( hdc != NULL );

	//set the viewport based on the monitor rect's relation to the primary monitor
	CRect rect;
	GetMonitorRect(&rect);

	::SetViewportOrgEx(hdc, -rect.left, -rect.top, NULL);
	::SetViewportExtEx(hdc, rect.Width(), rect.Height(), NULL);

	return hdc;
}

int CMonitor::GetBitsPerPixel() const
{
	HDC hdc = CreateDC();
	int ret = ::GetDeviceCaps(hdc, BITSPIXEL) * ::GetDeviceCaps(hdc, PLANES);
	VERIFY( ::DeleteDC( hdc ) );

	return ret;
}

void CMonitor::GetName(CString& string) const
{
	ASSERT( IsMonitor() );

	MONITORINFOEX mi;
	mi.cbSize = sizeof (mi);
	::GetMonitorInfo(m_hMonitor, &mi);

	string = mi.szDevice;
}

//
// these methods return true if any part of the item intersects the monitor rect
BOOL CMonitor::IsOnMonitor(const POINT pt) const
{
	CRect rect;
	GetMonitorRect(rect);

	return rect.PtInRect(pt);
}

BOOL CMonitor::IsOnMonitor(const CWnd* pWnd) const
{
	CRect rect;
	GetMonitorRect(rect);

	ASSERT( ::IsWindow( pWnd->GetSafeHwnd() ) );
	CRect wndRect;
	pWnd->GetWindowRect(&wndRect);

	return rect.IntersectRect(rect, wndRect);
}

BOOL CMonitor::IsOnMonitor(const LPRECT lprc) const
{
	CRect rect;
	GetMonitorRect(rect);

	return rect.IntersectRect(rect, lprc);
}

void CMonitor::GetMonitorRect(LPRECT lprc) const
{
	ASSERT( IsMonitor() );

	MONITORINFO mi;
	RECT rc;

	mi.cbSize = sizeof (mi);
	::GetMonitorInfo(m_hMonitor, &mi);
	rc = mi.rcMonitor;

	::SetRect(lprc, rc.left, rc.top, rc.right, rc.bottom);
}

//
// the work area does not include the start bar
void CMonitor::GetWorkAreaRect(LPRECT lprc) const
{
	ASSERT( IsMonitor() );

	MONITORINFO mi;
	RECT rc;

	mi.cbSize = sizeof (mi);
	::GetMonitorInfo(m_hMonitor, &mi);
	rc = mi.rcWork;

	::SetRect(lprc, rc.left, rc.top, rc.right, rc.bottom);
}

//these two center methods are adapted from David Campbell's
//MSJ article (see comment at the top of the header file)
void CMonitor::CenterRectToMonitor(LPRECT lprc, const BOOL UseWorkAreaRect) const
{
	int w = lprc->right - lprc->left;
	int h = lprc->bottom - lprc->top;

	CRect rect;
	if (UseWorkAreaRect) GetWorkAreaRect(&rect);
	else GetMonitorRect(&rect);

	lprc->left = rect.left + (rect.Width() - w) / 2;
	lprc->top = rect.top + (rect.Height() - h) / 2;
	lprc->right = lprc->left + w;
	lprc->bottom = lprc->top + h;
}

void CMonitor::CenterWindowToMonitor(CWnd* const pWnd, const BOOL UseWorkAreaRect) const
{
	ASSERT( IsMonitor() );
	ASSERT( pWnd );
	ASSERT( ::IsWindow( pWnd->m_hWnd ) );

	CRect rect;
	pWnd->GetWindowRect(&rect);
	CenterRectToMonitor(&rect, UseWorkAreaRect);
	pWnd->SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}

void CMonitor::ClipRectToMonitor(LPRECT lprc, const BOOL UseWorkAreaRect) const
{
	int w = lprc->right - lprc->left;
	int h = lprc->bottom - lprc->top;

	CRect rect;
	if (UseWorkAreaRect) GetWorkAreaRect(&rect);
	else GetMonitorRect(&rect);

	lprc->left = max( rect.left, min( rect.right - w, lprc->left ) );
	lprc->top = max( rect.top, min( rect.bottom - h, lprc->top ) );
	lprc->right = lprc->left + w;
	lprc->bottom = lprc->top + h;
}

//
// is the instance the primary monitor
BOOL CMonitor::IsPrimaryMonitor() const
{
	ASSERT( IsMonitor() );

	MONITORINFO mi;

	mi.cbSize = sizeof (mi);
	::GetMonitorInfo(m_hMonitor, &mi);

	return mi.dwFlags == MONITORINFOF_PRIMARY;
}

//
// is the instance currently attached to a valid monitor handle
BOOL CMonitor::IsMonitor() const
{
	return CMonitors::IsMonitor(m_hMonitor);
}
